package com.clouddms.service.security;

import com.clouddms.service.common.IService;
import com.clouddms.service.common.ServiceStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 文件完整性校验服务
 * 提供多种哈希算法的文件完整性校验
 *
 * @author Cloud-DMS Team
 * @version 1.0.0
 */
@Slf4j
@Service
public class FileIntegrityService implements IService {

    // 支持的哈希算法
    public enum HashAlgorithm {
        MD5("MD5", "MD5"),
        SHA1("SHA-1", "SHA1"),
        SHA256("SHA-256", "SHA256"),
        SHA512("SHA-512", "SHA512");

        private final String algorithmName;
        private final String displayName;

        HashAlgorithm(String algorithmName, String displayName) {
            this.algorithmName = algorithmName;
            this.displayName = displayName;
        }

        public String getAlgorithmName() { return algorithmName; }
        public String getDisplayName() { return displayName; }
    }

    // 文件校验缓存
    private final Map<String, FileIntegrityInfo> integrityCache = new ConcurrentHashMap<>();
    
    // 校验统计
    private long totalVerifications = 0;
    private long successfulVerifications = 0;
    private long failedVerifications = 0;

    // === IService 接口实现 ===

    @Override
    public String getServiceName() {
        return "fileIntegrity";
    }

    @Override
    public String getDisplayName() {
        return "文件完整性校验";
    }

    @Override
    public String getDescription() {
        return "提供MD5、SHA256等多种算法的文件完整性校验，确保文件传输和存储的可靠性";
    }

    @Override
    public ServiceStatus getStatus() {
        return ServiceStatus.RUNNING;
    }

    @Override
    public int getPriority() {
        return 28;
    }

    // === 文件完整性校验方法 ===

    /**
     * 计算文件的多种哈希值
     *
     * @param filePath 文件路径
     * @param algorithms 要计算的哈希算法
     * @return 文件完整性信息
     */
    public FileIntegrityInfo calculateFileIntegrity(String filePath, HashAlgorithm... algorithms) {
        try {
            log.debug("开始计算文件完整性: {}", filePath);

            Path path = Paths.get(filePath);
            if (!Files.exists(path)) {
                log.warn("文件不存在: {}", filePath);
                return FileIntegrityInfo.failure("文件不存在");
            }

            Set<HashAlgorithm> algorithmSet = algorithms.length > 0 ? 
                new HashSet<>(Arrays.asList(algorithms)) : 
                new HashSet<>(Arrays.asList(HashAlgorithm.values()));

            Map<HashAlgorithm, String> hashes = new HashMap<>();
            long fileSize = Files.size(path);
            long startTime = System.currentTimeMillis();

            // 一次读取计算所有哈希
            try (InputStream is = Files.newInputStream(path)) {
                Map<HashAlgorithm, MessageDigest> digestMap = new HashMap<>();
                
                // 初始化所有需要的MessageDigest
                for (HashAlgorithm algorithm : algorithmSet) {
                    try {
                        MessageDigest digest = MessageDigest.getInstance(algorithm.getAlgorithmName());
                        digestMap.put(algorithm, digest);
                    } catch (Exception e) {
                        log.warn("不支持的哈希算法: {}", algorithm.getDisplayName());
                    }
                }

                // 读取文件并更新所有摘要
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    for (MessageDigest digest : digestMap.values()) {
                        digest.update(buffer, 0, bytesRead);
                    }
                }

                // 获取最终的哈希值
                for (Map.Entry<HashAlgorithm, MessageDigest> entry : digestMap.entrySet()) {
                    byte[] hashBytes = entry.getValue().digest();
                    String hashHex = bytesToHex(hashBytes);
                    hashes.put(entry.getKey(), hashHex);
                }
            }

            long calculationTime = System.currentTimeMillis() - startTime;

            FileIntegrityInfo info = FileIntegrityInfo.success(filePath, fileSize, hashes, calculationTime);
            
            // 缓存结果
            integrityCache.put(filePath, info);
            
            log.debug("文件完整性计算完成: {}, 耗时: {}ms", filePath, calculationTime);
            return info;

        } catch (Exception e) {
            log.error("计算文件完整性失败: {}", e.getMessage(), e);
            return FileIntegrityInfo.failure("计算失败: " + e.getMessage());
        }
    }

    /**
     * 验证文件完整性
     *
     * @param filePath 文件路径
     * @param expectedHash 期望的哈希值
     * @param algorithm 哈希算法
     * @return 验证结果
     */
    public IntegrityVerificationResult verifyFileIntegrity(String filePath, String expectedHash, HashAlgorithm algorithm) {
        totalVerifications++;
        
        try {
            log.info("验证文件完整性: file={}, algorithm={}", filePath, algorithm.getDisplayName());

            if (expectedHash == null || expectedHash.trim().isEmpty()) {
                failedVerifications++;
                return IntegrityVerificationResult.failure("期望哈希值为空");
            }

            // 计算实际哈希
            FileIntegrityInfo integrity = calculateFileIntegrity(filePath, algorithm);
            if (!integrity.isSuccess()) {
                failedVerifications++;
                return IntegrityVerificationResult.failure("计算文件哈希失败: " + integrity.getErrorMessage());
            }

            String actualHash = integrity.getHash(algorithm);
            if (actualHash == null) {
                failedVerifications++;
                return IntegrityVerificationResult.failure("无法获取文件哈希值");
            }

            // 比较哈希值（忽略大小写）
            boolean isValid = expectedHash.equalsIgnoreCase(actualHash);
            
            if (isValid) {
                successfulVerifications++;
                log.info("文件完整性验证通过: {}", filePath);
                return IntegrityVerificationResult.success(actualHash, algorithm);
            } else {
                failedVerifications++;
                log.warn("文件完整性验证失败: file={}, expected={}, actual={}", 
                    filePath, expectedHash, actualHash);
                return IntegrityVerificationResult.failure(
                    String.format("哈希值不匹配 - 期望: %s, 实际: %s", expectedHash, actualHash));
            }

        } catch (Exception e) {
            failedVerifications++;
            log.error("文件完整性验证异常: {}", e.getMessage(), e);
            return IntegrityVerificationResult.failure("验证异常: " + e.getMessage());
        }
    }

    /**
     * 批量验证文件完整性
     *
     * @param fileIntegrityMap 文件路径和期望哈希值的映射
     * @param algorithm 哈希算法
     * @return 批量验证结果
     */
    public BatchVerificationResult batchVerifyIntegrity(Map<String, String> fileIntegrityMap, HashAlgorithm algorithm) {
        log.info("开始批量验证文件完整性: {} 个文件, 算法: {}", 
            fileIntegrityMap.size(), algorithm.getDisplayName());

        List<IntegrityVerificationResult> results = new ArrayList<>();
        int successCount = 0;
        int failCount = 0;

        for (Map.Entry<String, String> entry : fileIntegrityMap.entrySet()) {
            String filePath = entry.getKey();
            String expectedHash = entry.getValue();

            IntegrityVerificationResult result = verifyFileIntegrity(filePath, expectedHash, algorithm);
            result.setFilePath(filePath);
            results.add(result);

            if (result.isValid()) {
                successCount++;
            } else {
                failCount++;
            }
        }

        return new BatchVerificationResult(results, successCount, failCount);
    }

    /**
     * 获取文件完整性信息（从缓存或重新计算）
     *
     * @param filePath 文件路径
     * @param useCache 是否使用缓存
     * @return 文件完整性信息
     */
    public FileIntegrityInfo getFileIntegrity(String filePath, boolean useCache) {
        if (useCache && integrityCache.containsKey(filePath)) {
            FileIntegrityInfo cached = integrityCache.get(filePath);
            
            // 检查文件是否被修改
            try {
                Path path = Paths.get(filePath);
                if (Files.exists(path) && Files.size(path) == cached.getFileSize()) {
                    log.debug("使用缓存的文件完整性信息: {}", filePath);
                    return cached;
                }
            } catch (Exception e) {
                log.warn("检查缓存文件状态失败: {}", filePath);
            }
        }

        return calculateFileIntegrity(filePath);
    }

    /**
     * 清理完整性缓存
     *
     * @param filePath 文件路径，null表示清理所有
     */
    public void clearIntegrityCache(String filePath) {
        if (filePath == null) {
            int cacheSize = integrityCache.size();
            integrityCache.clear();
            log.info("清理所有完整性缓存: {} 项", cacheSize);
        } else {
            integrityCache.remove(filePath);
            log.debug("清理文件完整性缓存: {}", filePath);
        }
    }

    /**
     * 获取校验统计信息
     */
    public VerificationStatistics getVerificationStatistics() {
        return new VerificationStatistics(totalVerifications, successfulVerifications, failedVerifications);
    }

    // === 私有辅助方法 ===

    /**
     * 字节数组转十六进制字符串
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }

    // === 内部类 ===

    /**
     * 文件完整性信息
     */
    public static class FileIntegrityInfo {
        private boolean success;
        private String filePath;
        private long fileSize;
        private Map<HashAlgorithm, String> hashes;
        private long calculationTime;
        private String errorMessage;
        private long timestamp;

        private FileIntegrityInfo() {
            this.timestamp = System.currentTimeMillis();
        }

        public static FileIntegrityInfo success(String filePath, long fileSize, 
                                              Map<HashAlgorithm, String> hashes, long calculationTime) {
            FileIntegrityInfo info = new FileIntegrityInfo();
            info.success = true;
            info.filePath = filePath;
            info.fileSize = fileSize;
            info.hashes = new HashMap<>(hashes);
            info.calculationTime = calculationTime;
            return info;
        }

        public static FileIntegrityInfo failure(String errorMessage) {
            FileIntegrityInfo info = new FileIntegrityInfo();
            info.success = false;
            info.errorMessage = errorMessage;
            return info;
        }

        public boolean isSuccess() { return success; }
        public String getFilePath() { return filePath; }
        public long getFileSize() { return fileSize; }
        public Map<HashAlgorithm, String> getHashes() { return hashes; }
        public String getHash(HashAlgorithm algorithm) { return hashes != null ? hashes.get(algorithm) : null; }
        public long getCalculationTime() { return calculationTime; }
        public String getErrorMessage() { return errorMessage; }
        public long getTimestamp() { return timestamp; }
    }

    /**
     * 完整性验证结果
     */
    public static class IntegrityVerificationResult {
        private boolean valid;
        private String message;
        private String actualHash;
        private HashAlgorithm algorithm;
        private String filePath;
        private long verificationTime;

        private IntegrityVerificationResult() {
            this.verificationTime = System.currentTimeMillis();
        }

        public static IntegrityVerificationResult success(String actualHash, HashAlgorithm algorithm) {
            IntegrityVerificationResult result = new IntegrityVerificationResult();
            result.valid = true;
            result.message = "文件完整性验证通过";
            result.actualHash = actualHash;
            result.algorithm = algorithm;
            return result;
        }

        public static IntegrityVerificationResult failure(String message) {
            IntegrityVerificationResult result = new IntegrityVerificationResult();
            result.valid = false;
            result.message = message;
            return result;
        }

        // Getters and Setters
        public boolean isValid() { return valid; }
        public String getMessage() { return message; }
        public String getActualHash() { return actualHash; }
        public HashAlgorithm getAlgorithm() { return algorithm; }
        public String getFilePath() { return filePath; }
        public void setFilePath(String filePath) { this.filePath = filePath; }
        public long getVerificationTime() { return verificationTime; }
    }

    /**
     * 批量验证结果
     */
    public static class BatchVerificationResult {
        private final List<IntegrityVerificationResult> results;
        private final int successCount;
        private final int failCount;
        private final int totalCount;

        public BatchVerificationResult(List<IntegrityVerificationResult> results, int successCount, int failCount) {
            this.results = results;
            this.successCount = successCount;
            this.failCount = failCount;
            this.totalCount = results.size();
        }

        public List<IntegrityVerificationResult> getResults() { return results; }
        public int getSuccessCount() { return successCount; }
        public int getFailCount() { return failCount; }
        public int getTotalCount() { return totalCount; }
        public double getSuccessRate() { return totalCount > 0 ? (double) successCount / totalCount * 100 : 0; }
    }

    /**
     * 验证统计信息
     */
    public static class VerificationStatistics {
        private final long totalVerifications;
        private final long successfulVerifications;
        private final long failedVerifications;

        public VerificationStatistics(long total, long successful, long failed) {
            this.totalVerifications = total;
            this.successfulVerifications = successful;
            this.failedVerifications = failed;
        }

        public long getTotalVerifications() { return totalVerifications; }
        public long getSuccessfulVerifications() { return successfulVerifications; }
        public long getFailedVerifications() { return failedVerifications; }
        public double getSuccessRate() { 
            return totalVerifications > 0 ? (double) successfulVerifications / totalVerifications * 100 : 0; 
        }
    }
}